package com.netease.demo.shortvideo.server;

import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.annotation.Nullable;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.netease.demo.shortvideo.DemoCache;
import com.netease.demo.shortvideo.config.Preferences;
import com.netease.demo.shortvideo.server.entity.AddVideoResponseEntity;
import com.netease.demo.shortvideo.server.entity.VideoInfoEntity;
import com.netease.nim.uikit.common.http.NimHttpClient;
import com.netease.nim.uikit.common.util.log.LogUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 与应用服务器通信
 */
public class DemoServerHttpClient {
    private static final String TAG = "ContactHttpClient";


    //public static final String API_SERVER = "http://vcloud.163.com/appdemo"; //线上
//    public static final String API_SERVER = "http://106.2.44.145:8181/appdemo"; //测试
    public static final String API_SERVER = "http://106.2.44.63/appdemo"; //开发
    public static final String TEST_HOST = "vcloud.163.com"; //用于 ping ip地址,测试网络是否连通

    // code
    private static final int RESULT_CODE_SUCCESS = 200;

    // api
    private static final String API_NAME_REGISTER = "/vcuser/reg";
    private static final String API_LOGIN = "/vcuser/login";
    private static final String API_LOGOUT = "/vcuser/logout";

    //点播相关 api
    private static final String API_VIDEO_INFO_GET = "/vcvod/videoinfoget";
    private static final String API_VIDEO_ADD = "/vcvod/videoadd";
    private static final String API_VIDEO_DELETE = "/vcvod/videodelete";

    // header
    private static final String HEADER_KEY_APP_KEY = "appkey";
    private static final String HEADER_CONTENT_TYPE = "Content-Type";
    private static final String HEADER_USER_AGENT = "User-Agent";

    // request login
    private static final String REQUEST_USER_NAME = "username";
    private static final String REQUEST_NICK_NAME = "nickname";
    private static final String REQUEST_PASSWORD = "password";

    //request room
    public static final String REQUEST_SID = "sid";
    public static final String REQUEST_ROOM_ID = "roomid";
    public static final String REQUEST_PULL_URL = "pullUrl";
    public static final String CID = "cid";
    public static final String REQUEST_DEVICE_ID = "deviceid";

    //request vod
    public static final String VID = "vid";
    public static final String FILE_NAME = "name";
    public static final String VIDEO_FORMAT = "format";

    // result
    private static final String RESULT_KEY_CODE = "code";
    private static final String RESULT_KEY_ERROR_MSG = "msg";

    private static final String RESULT_KEY_RET = "ret";
    private static final String RESULT_KEY_TOKEN = "";
    private static final String RESULT_KEY_VOD_TOKEN = "vodtoken";
    private static final String RESULT_KEY_SID = "sid";

    private static final String RESULT_KEY_COMPLETE_TIME = "completeTime";
    private static final String RESULT_KEY_CREATE_TIME = "createTime";
    private static final String RESULT_KEY_ORIG_URL = "origUrl";
    private static final String RESULT_KEY_UPDATE_TIME = "updateTime";
    private static final String RESULT_KEY_SNAPSHOT_URL = "snapshotUrl";

    public interface DemoServerHttpCallback<T> {
        void onSuccess(T t);

        void onFailed(int code, String errorMsg);
    }

    private static DemoServerHttpClient instance;

    public static synchronized DemoServerHttpClient getInstance() {
        if (instance == null) {
            instance = new DemoServerHttpClient();
        }

        return instance;
    }

    private DemoServerHttpClient() {
        NimHttpClient.getInstance().init(DemoCache.getContext());
    }

    /**
     * 向应用服务器创建账号（注册账号）
     * 由应用服务器调用WEB SDK接口将新注册的用户数据同步到云信服务器
     */
    public void register(String account, String nickName, String password, final DemoServerHttpCallback<Void> callback) {
        String url = API_SERVER + API_NAME_REGISTER;
        //password = MD5.getStringMD5(password);
        try {
            nickName = URLEncoder.encode(nickName, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        Map<String, String> headers = new HashMap<>(1);
        String appKey = readAppKey();
        headers.put(HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8");
//        headers.put(HEADER_USER_AGENT, "nim_demo_android");
//        headers.put(HEADER_KEY_APP_KEY, appKey);

        StringBuilder body = new StringBuilder();
        body.append(REQUEST_USER_NAME).append("=").append(account.toLowerCase()).append("&")
                .append(REQUEST_NICK_NAME).append("=").append(nickName).append("&")
                .append(REQUEST_PASSWORD).append("=").append(password);

        NimHttpClient.getInstance().execute(url, headers, body.toString(), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable exception) {
                if (code != 200 || exception != null) {
                    LogUtil.e(TAG, "register failed : code = " + code + ", errorMsg = "
                            + (exception != null ? exception.getMessage() : "null"));
                    if (callback != null) {
                        callback.onFailed(code, exception != null ? exception.getMessage() : "null");
                    }
                    return;
                }

                try {
                    JSONObject resObj = JSONObject.parseObject(response);
                    int resCode = resObj.getIntValue(RESULT_KEY_CODE);
                    if (resCode == RESULT_CODE_SUCCESS) {
                        callback.onSuccess(null);
                    } else {
                        String error = resObj.getString(RESULT_KEY_ERROR_MSG);
                        callback.onFailed(resCode, error);
                    }
                } catch (JSONException e) {
                    callback.onFailed(-1, e.getMessage());
                }
            }
        });
    }


    /**
     * 向应用服务器创建账号（注册账号）
     * 由应用服务器调用WEB SDK接口将新注册的用户数据同步到云信服务器
     */
    public void login(String account, String password, final DemoServerHttpCallback<Void> callback) {
        String url = API_SERVER + API_LOGIN;

        Map<String, String> headers = new HashMap<>(1);
        String appKey = readAppKey();
        headers.put(HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8");
//        headers.put(HEADER_USER_AGENT, "nim_demo_android");
//        headers.put(HEADER_KEY_APP_KEY, appKey);

        StringBuilder body = new StringBuilder();
        body.append(REQUEST_USER_NAME).append("=").append(account.toLowerCase()).append("&")
                .append(REQUEST_PASSWORD).append("=").append(password);

        NimHttpClient.getInstance().execute(url, headers, body.toString(), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable exception) {
                if (code != 200 || exception != null) {
                    LogUtil.e(TAG, "register failed : code = " + code + ", errorMsg = "
                            + (exception != null ? exception.getMessage() : "null"));
                    if (callback != null) {
                        callback.onFailed(code, exception != null ? exception.getMessage() : "null");
                    }
                    return;
                }

                try {
                    JSONObject resObj = JSONObject.parseObject(response);
                    int resCode = resObj.getIntValue(RESULT_KEY_CODE);
                    if (resCode == RESULT_CODE_SUCCESS) {
                        JSONObject retObj = resObj.getJSONObject(RESULT_KEY_RET);
                        DemoCache.setAccount(resObj.getString(REQUEST_USER_NAME));
                        DemoCache.setVodtoken(retObj.getString(RESULT_KEY_VOD_TOKEN));
                        DemoCache.setSid(retObj.getString(RESULT_KEY_SID));

                        Preferences.saveVodToken(retObj.getString(RESULT_KEY_VOD_TOKEN));
                        callback.onSuccess(null);
                    } else {
                        String error = resObj.getString(RESULT_KEY_ERROR_MSG);
                        callback.onFailed(resCode, error);
                    }
                } catch (JSONException e) {
                    callback.onFailed(-1, e.getMessage());
                } catch (Exception e){
                    callback.onFailed(-1, e.getMessage());
                }
            }
        });
    }

    /**
     * 向应用服务器发送登出请求
     */
    public void logout(){
        String url = API_SERVER + API_LOGOUT;

        Map<String, String> headers = new HashMap<>(1);
        headers.put(HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8");

        StringBuilder body = new StringBuilder();
        body.append(REQUEST_SID).append("=").append(DemoCache.getSid());

        NimHttpClient.getInstance().execute(url, headers, body.toString(), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable exception) {
                //登出不保证是否成功
            }
        });
    }

    /**
     * 添加上传的视频ID，客户端上传成功后须调用
     * 服务端返回成功,即通知点播服务器开始转码
     * @param vid 	视频ID
     * @param name  视频文件名
     * @param callback
     */
    public void addVideo(long vid, String name, final DemoServerHttpCallback<AddVideoResponseEntity> callback){
        String url = API_SERVER + API_VIDEO_ADD;
        Map<String, Object> body = new HashMap<>();
        body.put(REQUEST_SID, DemoCache.getSid());
        body.put(VID, vid);
        body.put(FILE_NAME, name);

        NimHttpClient.getInstance().execute(url, getParamsString(body), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable e) {

                if(code!=200 || e!=null){
                    callback.onFailed(code, e!=null? e.getMessage() : "error code :" + code);
                    return;
                }

                try{
                    JSONObject jsonObject = JSONObject.parseObject(response);
                    int resCode = jsonObject.getIntValue(RESULT_KEY_CODE);
                    if(resCode == 200) {
                        JSONObject retObj = jsonObject.getJSONObject(RESULT_KEY_RET);
                        AddVideoResponseEntity entity = new AddVideoResponseEntity();
                        entity.setVideoCount(retObj.getInteger("videoCount"));
                        JSONObject videoClip = retObj.getJSONObject("videoclip");
                        entity.setCompleteTime(videoClip.getLongValue(RESULT_KEY_COMPLETE_TIME));
                        entity.setCreateTime(videoClip.getLongValue(RESULT_KEY_CREATE_TIME));
                        entity.setOrigUrl(videoClip.getString(RESULT_KEY_ORIG_URL));
                        entity.setSnapshotUrl(videoClip.getString(RESULT_KEY_SNAPSHOT_URL));
                        entity.setUpdateTime(videoClip.getLongValue(RESULT_KEY_UPDATE_TIME));
                        entity.setVid(videoClip.getLongValue(VID));
                        callback.onSuccess(entity);
                    }else{
                        callback.onFailed(resCode, jsonObject.getString(RESULT_KEY_ERROR_MSG));
                    }
                } catch (Exception exception){
                    callback.onFailed(-1, exception.getMessage());
                }
            }
        });
    }

    /**
     *
     * 删除已上传视频
     * 参数只有vid，则删除原视频及其转码视频，该视频所有信息均删除
     * 参数除vid还有format，则只删除单个转码视频
     * @param vid 视频ID
     * @param format 视频转码格式（1表示标清mp4，2表示高清mp4，3表示超清mp4，4表示标清flv，5表示高清flv，6表示超清flv，7表示标清hls，8表示高清hls，9表示超清hls）
     */
    public void videoDelete(long vid, @Nullable Integer format,final DemoServerHttpCallback<Void> callback){
        String url = API_SERVER + API_VIDEO_DELETE;
        Map<String, Object> body = new HashMap<>();
        body.put(REQUEST_SID, DemoCache.getSid());
        body.put(VID, vid);
        if(format!=null){
            body.put(VIDEO_FORMAT, format);
        }

        NimHttpClient.getInstance().execute(url, getParamsString(body), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable e) {
                if(code!=200 || e!=null){
                    callback.onFailed(code, e!=null? e.getMessage() : "error code :" + code);
                    return;
                }

                try{
                    JSONObject jsonObject = JSONObject.parseObject(response);
                    int resCode = jsonObject.getIntValue(RESULT_KEY_CODE);
                    if(resCode == 200 || resCode == 1644){
                        //{"code":1644,"msg":"视频不存在"} 多端登录删除同一视频的情况
                        callback.onSuccess(null);
                    }else{
                        callback.onFailed(resCode, jsonObject.getString(RESULT_KEY_ERROR_MSG));
                    }
                } catch (Exception exception){
                    callback.onFailed(-1, exception.getMessage());
                }

            }
        });
    }

    /**
     * 获取所有已上传视频的信息。
     * 如果有vid参数，则只返回该视频信息，否则返回该用户所有视频信息。
     * @param vid 	视频ID
     */
    public void videoInfoGet(@Nullable Long vid, final DemoServerHttpCallback<List<VideoInfoEntity>> callback){
        String url = API_SERVER + API_VIDEO_INFO_GET;
        Map<String, Object> body = new HashMap<>();
        body.put(REQUEST_SID, DemoCache.getSid());
        if(vid != null){
            body.put(VID, vid);
        }

        NimHttpClient.getInstance().execute(url, getParamsString(body), new NimHttpClient.NimHttpCallback() {
            @Override
            public void onResponse(String response, int code, Throwable e) {
                if(code!=200 || e!=null){
                    callback.onFailed(code, e!=null? e.getMessage() : "error code :" + code);
                    return;
                }
                try {
                    JSONObject jsonObj = JSONObject.parseObject(response);
                    int resCode = jsonObj.getIntValue(RESULT_KEY_CODE);
                    if(resCode== 200) {
                        JSONObject retObj = jsonObj.getJSONObject(RESULT_KEY_RET);
                        List<VideoInfoEntity> list = JSON.parseArray(retObj.get("list").toString(), VideoInfoEntity.class);
                        callback.onSuccess(list);
                    }else{
                        callback.onFailed(resCode, jsonObj.getString(RESULT_KEY_ERROR_MSG));
                    }
                }catch (Exception e1){
                    callback.onFailed(-1, e1.getMessage());
                }
            }
        });
    }

    public static String getParamsString(Map<String, Object> maps){
        StringBuilder builder = new StringBuilder();

        boolean first = true;
        for (Map.Entry entry : maps.entrySet()) {
            if(first) {
                first = false;
            }else{
                builder.append("&");
            }

            builder.append(entry.getKey())
                   .append("=");
            if(entry.getValue() instanceof List) {
                boolean innerFirst = true;
                for (Object object: (List)entry.getValue()) {
                    if(innerFirst){
                        innerFirst = false;
                    }else{
                        builder.append(",");
                    }
                    builder.append(object);
                }
            }else{
                builder.append(entry.getValue());
            }
        }

        return builder.toString();
    }



    public static String readAppKey() {
        try {
            ApplicationInfo appInfo = DemoCache.getContext().getPackageManager()
                    .getApplicationInfo(DemoCache.getContext().getPackageName(), PackageManager.GET_META_DATA);
            if (appInfo != null) {
                return appInfo.metaData.getString("com.netease.nim.appKey");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
